#pragma once
#include <iostream>
#include <mutex>
#include <cassert>
#include <pthread.h>
#include "model_db.hpp"
#include "view.hpp"
#include "../comm/httplib.h"
#include <jsoncpp/json/json.h>

using namespace httplib;

// 在control模块中添加负载均衡控制
//  control 对view和model模块进行控制

const std::string conf_path = "./conf/machine.conf";

class Machine
{
public:
    Machine() : _ip("0.0.0.0"), _port(0), _load(0), _pmtx(nullptr) {}
    void IncLoad()
    {
        // 增加负载
        if (_pmtx)
            _pmtx->lock();
        ++_load;
        if (_pmtx)
            _pmtx->unlock();
    }

    void DecLoad()
    {
        // 减小负载
        if (_pmtx)
            _pmtx->lock();
        --_load;
        if (_pmtx)
            _pmtx->unlock();
    }

    uint64_t GetLoad()
    {
        if (_pmtx)
            _pmtx->lock();
        uint64_t load = _load;
        if (_pmtx)
            _pmtx->unlock();
        return load;
    }

    void ResetLoad()
    {
        if (_pmtx)
            _pmtx->lock();
        _load = 0;
        if (_pmtx)
            _pmtx->unlock();
    }

    ~Machine()
    {
    }

public:
    std::string _ip;
    int _port;
    uint64_t _load;    // 主机的负载
    std::mutex *_pmtx; // mutex禁止拷贝，因此用指针
};

class LoadBlance
{
private:
    std::vector<Machine> _machines;
    std::vector<int> _Online; // 存储上线机器的序号
    std::vector<int> _Offline;
    std::mutex _mtx; // 保证Online, Offline, LoadBlance的安全
public:
    LoadBlance()
    {
        assert(LoadConf());
        logMessage(NORMAL, "加载主机配置文件成功!!!");
    }
    bool LoadConf()
    {
        // 加载主机配置文件
        std::ifstream in(conf_path);
        if (!in.is_open())
        {
            logMessage(FATAL, "%s", "主机配置文件打开出错");
            return false;
        }
        std::cout << "开始加载主机配置文件" << std::endl;
        std::string line;
        while (std::getline(in, line))
        {
            // 从in中逐行读取,并交由UTIL切分
            std::vector<std::string> tokens;
            StringUtil::split(line, &tokens, ":");
            if (tokens.size() != 2)
            {
                logMessage(ERROR, "%s", "存在主机配置文件格式有误");
                continue; // 如果存在主机配置有误，应当尽快调整
            }
            //
            for (const auto &it : tokens)
            {
                std::cout << it << " ";
            }
            std::cout << std::endl;

            Machine m;
            m._ip = tokens[0];
            m._port = atoi(tokens[1].c_str());
            m._pmtx = new std::mutex();

            // Online中存储着对应的主机的下标, 第一台主机的下标是0
            _Online.push_back(_machines.size());
            std::cout << "添加一台主机成功, id: " << _machines.size() << std::endl;
            _machines.push_back(m);
        }
        in.close();
        logMessage(NORMAL, "添加判题主机完毕，当前主机库中主机数：%d", _Online.size());
        return true;
    }

    // 第一个参数返回主机id，第二个参数返回主机地址
    bool SmartChoice(int *id, Machine **m)
    {
        // Online, Offline是临界资源, 需要加锁
        _mtx.lock();
        // 负载均衡算法
        // 1. 随机数法+哈希
        // 2. 轮询+hash
        int online_num = _Online.size();
        if (online_num == 0)
        {
            // 这里可以添加一个检查 TODO
            // 所有主机都已经挂掉了
            _mtx.unlock();
            logMessage(FATAL, "%s", "主机全部离线");
            return false;
        }
        // 挑一台负载最小的主机返回
        uint64_t min_load = _machines[_Online[0]].GetLoad();
        *id = _Online[0];
        *m = &_machines[_Online[0]];
        for (int i = 1; i < online_num; i++)
        {
            if (min_load < _machines[_Online[i]].GetLoad())
            {
                min_load = _machines[_Online[i]].GetLoad();
                *id = _Online[i];
                *m = &_machines[_Online[i]];
            }
        }
        _mtx.unlock();
        return true;
    }

    bool online(int id)
    {
        // 上线对应的主机
        return true;
    }

    bool offline(int id)
    {
        // 下线对应主机
        _mtx.lock();
        for (auto it = _Online.begin(); it != _Online.end(); it++)
        {
            if (*it == id)
            {
                _machines[id].ResetLoad();
                _Online.erase(it);
                _Offline.push_back(*it);
                break;
            }
        }
        _mtx.unlock();
        return true;
    }
    ~LoadBlance() {}
};
class Control
{
    Model _model;
    View _view;
    LoadBlance _blance; // 实现负载均衡
public:
    Control() {}

    // 返回特定题解网页
    std::string getSolve(int number)
    {
        Solve s;
        if (!_model.getSolve(number, &s))
        {
            logMessage(FATAL, "%s, [%s,%d]", "查询题解失败!!!", __FILE__, __LINE__);
            return "题解不存在";
        }

        // 渲染网页返回
        std::string html = _view.expandSolve(s);
        logMessage(FATAL, "%s, [%s,%d]", "渲染题解网页完成(是否成功未知)!!!", __FILE__, __LINE__);
        return html;
    }

    // 返回题目列表网页
    std::string getALLQuestionsOfHtml()
    {
        std::vector<Question> tokens;
        if (!_model.getAllQuestions(&tokens))
        {
            std::cout << "contrlo模块中获取题目列表失败" << std::endl;
            return "";
        }
        std::string html = _view.expandAllQuestions(tokens);
        std::cout << "contrlo模块中获取所有题目成功" << std::endl;
        return html;
    }

    // 返回某一道题目网页
    std::string getOneQuestionOfHtml(int number)
    {
        Question q;
        if (!_model.getOneQuestion(number, &q))
        {
            std::cout << "contrlo模块中指定题目失败" << std::endl;
            return "";
        }
        // 成功
        std::string html = _view.expandOneQuestion(q);
        std::cout << "contrlo模块中获取指定题目成功" << std::endl;
        return html;
    }

    // 返回讨论界面
    std::string getDiscussions()
    {
        std::vector<Discussion> out;
        if (!_model.getAllDiscussion(&out))
        {
            logMessage(WARNING, "%s, [%s,%d]", "获取全部帖子失败!!!", __FILE__, __LINE__);
            return "";
        }
        std::string html = _view.expandDiscussions(out);
        logMessage(WARNING, "%s, [%s,%d]", "获取帖子成功!!!", __FILE__, __LINE__);
        return html;
    }
    
    // 返回特定一篇文章界面
    std::string getDiscussion(int number)
    {
        // 1. 获取数据
        Discussion out;
        if (!_model.getADiscussion(number, &out))
        {
            logMessage(WARNING, "%s, %d, %s, [%s,%d]", "获取", number, "号帖子失败!!!", __FILE__, __LINE__);
            return "";
        }
        // 2. 渲染网页
        std::string html = _view.expandDiscussion(out);
        logMessage(DEBUG, "%s, %d, %s, [%s,%d]", "获取", number, "号帖子成功!!!", __FILE__, __LINE__);
        return html;
    }
    
    // 判题
    void judge(const std::string &number, const std::string in_json, std::string *out_json)
    {
        // 对得到的json做反序列化
        Question q;
        if (!_model.getOneQuestion(atoi(number.c_str()), &q))
        {
            return;
        }
        Json::Reader reader;
        Json::Value in_value;
        reader.parse(in_json, in_value); // 把从网络中读取到的数据解析出来
        std::string code = in_value["code"].asString();

        // 拼接测试代码
        Json::Value complie_value;
        complie_value["code"] = q.head + "\n" + code + "\n" + q.tail;
        complie_value["input"] = in_value["input"];
        complie_value["cpu_limit"] = in_value["cpu_limit"];
        complie_value["mem_limit"] = in_value["mem_limit"];

        // 序列化
        Json::FastWriter writer;
        std::string request_str = writer.write(complie_value);

        // 挑选主机
        // 一直挑选
        while (true)
        {
            int id = 0;
            Machine *m = nullptr;
            if (!_blance.SmartChoice(&id, &m))
            {
                // 到这里说明所有的主机都已经挂掉了，那么就没必要继续挑选了
                break;
            }
            logMessage(DEBUG, "选择到来自[%d]号端口的%d号主机, 该主机当前负载: %lld, [%s, %d]", m->_port, id, m->_load, __FILE__, __LINE__);
            // 到这里说明成功选择主机, 可以开始发送请求了
            Client cli(m->_ip, m->_port); // 使用httplib构造客户端
            std::cout << __FILE__ << __LINE__ << std::endl;
            m->IncLoad(); // 被选择需要增加负载
            std::cout << __FILE__ << __LINE__ << std::endl;
            if (auto res = cli.Post("/complie_and_run", request_str, "application/json;charset=utf-8")) // 使用post方法请求服务器
            {
                std::cout << __FILE__ << __LINE__ << std::endl;
                // post方法中会检验返回值是否为nullptr, 是则返回false
                if (res->status == 200)
                {
                    *(out_json) = res->body;
                    m->DecLoad();
                    break;
                }
                m->DecLoad();
            }
            else
            {
                std::cout << __FILE__ << __LINE__ << std::endl;
                // 请求失败, 需要将主机下线
                logMessage(NORMAL, "%s:%d", "该主机可能已经离线, id ", id);
                _blance.offline(id);
            }
        }
    }

    // 用户注册
    void toRegister(const std::string in_json, std::string *out_json)
    {
        // 对得到的json做反序列化
        Json::Reader reader;
        Json::Value in_value;
        reader.parse(in_json, in_value); // 把从网络中读取到的数据解析出来
        std::string username = in_value["username"].asString();
        std::string email = in_value["email"].asString();
        std::string password = in_value["password"].asString();

        // 检查数据库中是否存在相同用户，如果有就返回已存在，否则加入数据库
        std::string err_information;
        bool res = _model.toRegister(username, email, password, err_information);

        Json::Value out_value;
        if (res) out_value["status"] = "success";
        else
            out_value["status"] = "false";
        out_value["err_information"] = err_information;

        Json::StyledWriter writer;
        (*out_json) = writer.write(out_value);
    }

    // 登录验证
    void toLogin(const std::string in_json, std::string *out_json)
    {
        // 对得到的json做反序列化
        Json::Reader reader;
        Json::Value in_value;
        reader.parse(in_json, in_value); // 把从网络中读取到的数据解析出来
        std::string username = in_value["username"].asString();
        std::string password = in_value["password"].asString();


        std::string err_information;
        bool res = _model.toLogin(username, password, err_information);

        Json::Value out_value;
        if (res)
            out_value["status"] = "success";
        else
            out_value["status"] = "false";
        out_value["err_information"] = err_information;

        Json::StyledWriter writer;
        (*out_json) = writer.write(out_value);
    }

    // 获取提交的帖子
    void toPostArticle(const std::string in_json, std::string *out_json)
    {
        // 对得到的json做反序列化
        Json::Reader reader;
        Json::Value in_value;
        reader.parse(in_json, in_value); // 把从网络中读取到的数据解析出来
        std::string title = in_value["title"].asString();
        std::string author = in_value["author"].asString();
        std::string maintext = in_value["content"].asString();

        std::string err_information;
        // 添加到数据库中
        bool res = _model.addArticleToDB(title, author, maintext, err_information);

        Json::Value out_value;
        if (res)
            out_value["status"] = "success";
        else
            out_value["status"] = "false";
        out_value["err_information"] = err_information;

        Json::StyledWriter writer;
        (*out_json) = writer.write(out_value);
    }

    ~Control() {}
} 

;